#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>

#define WORKER_NUMBER 4

int in,out;
struct task {
	int is_end;
	char path[128];
	char string[128];
}buffer[WORKER_NUMBER];

int buffer_is_empty()
{
	return in == out;
}

int buffer_is_full()
{
	return (in + 1) % WORKER_NUMBER == out;
}

void get_task(struct task *task)
{
	task->is_end = buffer[out].is_end;
	strcpy(task->path, buffer[out].path);
	strcpy(task->string, buffer[out].string);
	out = (out + 1) % WORKER_NUMBER;
}

void put_task(int is_end, char *path, char *string)
{
	buffer[in].is_end = is_end;
	strcpy(buffer[in].path, path);
	strcpy(buffer[in].string, string);
	in = (in + 1) % WORKER_NUMBER;
}

void find_file(char *path, char *target)
{
	FILE *file = fopen(path, "r");

	char line[256];
	while(fgets(line, sizeof(line),file)) {
		if(strstr(line ,target))
			printf("%s: %s", path, line);
	}

	fclose(file);
}

pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;

void *worker_entry(void *arg)
{
	while(1) {
		pthread_mutex_lock(&mutex);
		while(buffer_is_empty())
			pthread_cond_wait(&full, &mutex);

		struct task task;
		get_task(&task);
		
		if(task.is_end) {
			pthread_cond_signal(&empty);
			pthread_mutex_unlock(&mutex);
			break;
		}
		//find_file(task.path, task.string);
		pthread_cond_signal(&empty);
		pthread_mutex_unlock(&mutex);

		find_file(task.path, task.string);
	}
}

void find_dir_2(char *path, char *target)
{
	char new_Path[512];

	DIR *dir = opendir(path);
	struct dirent *entry;
	while(entry = readdir(dir)) {
		if(strcmp(entry->d_name, ".") == 0) 
			continue;
		if(strcmp(entry->d_name, "..") == 0) 
			continue;

		bzero(new_Path, sizeof(new_Path));
		sprintf(new_Path, "%s/%s", path, entry->d_name);

		if(entry->d_type == DT_DIR) find_dir_2(new_Path, target);
		if(entry->d_type == DT_REG) {
			pthread_mutex_lock(&mutex);
			while(buffer_is_full())
				pthread_cond_wait(&empty, &mutex);
			
			put_task(0, new_Path, target);

			pthread_cond_signal(&full);
			pthread_mutex_unlock(&mutex);
		}
	}
	closedir(dir);
}

int main(int argc, char *argv[])
{
	if(argc != 3) {
		puts("Usage: pfind file string");
		return 0;
	}

	char *path = argv[1];
	char *string = argv[2];

	pthread_t worker_tid[2];

	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&empty, NULL);
	pthread_cond_init(&full, NULL);

	pthread_create(&worker_tid[0], NULL, worker_entry, NULL);
	pthread_create(&worker_tid[1], NULL, worker_entry, NULL);

	struct stat info;
	stat(path, &info);

	if(S_ISDIR(info.st_mode)) find_dir_2(path, string);
	else find_file(path, string);
	
	int i;
	for(i = 0; i < 2; i++) {
		pthread_mutex_lock(&mutex);
		while(buffer_is_full())
			pthread_cond_wait(&empty, &mutex);

		put_task(1, path, string);

		pthread_cond_signal(&full);
		pthread_mutex_unlock(&mutex);
	}

	pthread_join(worker_tid[0], NULL);
	pthread_join(worker_tid[1], NULL);

	return 0;
}
